#ifndef CAMERA_H
#define CAMERA_H

#include"hittable.h"
#include"material.h"


//define camera
class camera {
public:
	double aspect_ratio = 1.0;
	int image_width = 100;
	int samples_num = 10;
	int max_depth = 10;

	double vfov = 90; //view angle

	point3 lookfrom = point3(0, 0, 0);
	point3 lookat = point3(0, 0, -1);
	vec3 vup = vec3(0, 1, 0);

	double defocus_angle=0; //Variation angle

	double focus_dist=10;


	void render(hittable& world) {
		initialize();

		std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

		//get ray at each screen pixel
		for (int j = 0; j < image_height; j++) {
			std::clog << "\rScalings remaining: " << (image_height - j) << ' ' << std::flush;
			for (int i = 0; i < image_width; i++) {
				color pixel_color(0, 0, 0);
				for (int sam = 0; sam < samples_num; sam++) {
					ray _ray = get_ray(i, j);
					pixel_color += ray_color(_ray,max_depth, world);
				}

				write_color(std::cout, pixel_sample_scale* pixel_color);
			}
		}

		std::clog << "\rDone.          \n";
	}

private:
	int image_height;
	double pixel_sample_scale; //color scale factor
	point3 center;
	point3 pixel0_loc;
	vec3 delta_u;
	vec3 delta_v;
	vec3 u, v, w;
	vec3 defocus_disk_u;
	vec3 defocus_disk_v;


	void initialize() {
		image_height = int(image_width / aspect_ratio);
		image_height = (image_height < 1) ? 1 : image_height;
		pixel_sample_scale = 1.0 / samples_num;

		center = lookfrom;
		//auto focal_length = (lookfrom-lookat).length();
		auto theta = deg2rad(vfov);
		auto h = std::tan(theta / 2);



		auto viewport_height = 2 * h * focus_dist;
		auto viewport_width = viewport_height * (double(image_width) / image_height);

		//basis for camera coordinate
		w = unit_vector(lookfrom - lookat);
		u = unit_vector(cross(vup, w));
		v = cross(w, u);



		//Scanning start point
		auto viewport_u = viewport_width*u;
		auto viewport_v = viewport_height*(-v);

		//delta distance
		delta_u = viewport_u / image_width;
		delta_v = viewport_v / image_height;


		auto viewport_upper_left = center - (focus_dist*w) - viewport_u / 2 - viewport_v / 2;

		 pixel0_loc = viewport_upper_left + 0.5 * (delta_u + delta_v);

		 //defocus disk,out of which image get blurred
		 auto defocus_radius = focus_dist * std::tan(deg2rad(defocus_angle / 2));
		 defocus_disk_u = u * defocus_radius;
		 defocus_disk_v = v * defocus_radius;


	}


	//obtain ray
	ray get_ray(int i, int j) {
		//ray at pos (i,j)

		auto offset = sample_square();
		auto pixel_sample = pixel0_loc
			+ ((i + offset.x()) * delta_u)
			+ ((j + offset.y()) * delta_v);
		auto ray_origin = (defocus_angle<=0)?center: defocus_disk_sample();

		auto ray_dir = pixel_sample - ray_origin;

		return ray(ray_origin, ray_dir);
	}


	vec3 sample_square() const {
		//random pts in ranges([-0.5,0.5],[-0.5,0.5])
		return vec3(random_double() - 0.5, random_double() - 0.5,0);
	}

	point3 defocus_disk_sample() const {
		//random point on disk
		auto p = random_in_unit_disk();
		return center + (p[0] * defocus_disk_u) + (p[1] * defocus_disk_v);
	}




	color ray_color(const ray& _ray,int depth, const hittable& world) const {
		if (depth <= 0) return vec3(0,0,0);
		hit_record rec;

		if (world.hit(_ray, interval(0.001, inf), rec)) {
			ray scattered;
			color attenuation;

			if (rec.mat->scatter(_ray, rec, attenuation, scattered)) {
				return attenuation * ray_color(scattered, depth - 1, world);
			}

			return color(0,0,0);
		}

		vec3 unit_direction = unit_vector(_ray.direction());
		auto a = 0.5 * (unit_direction.y() + 1.0);
		return (1.0 - a) * color(1.0, 1.0, 1.0) + a * color(0.5, 0.7, 1.0);
	}

};


#endif // !CAMERA_H
